/*
 * JBoss, Home of Professional Open Source
 * Copyright 2006, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.fing.edu.uy.esbadp.core.entity;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;

import org.fing.edu.uy.esbadp.logger.LoggerADP;
import org.jboss.soa.esb.ConfigurationException;

/**
 * Objects of this class are to be used for run time configuration of ESB components
 * <p/>  This should gradually replace the ConfigTree class
 * <br/> It is a subset of Tree that accepts a Map of attributes, and a List of children
 * <br/> children can only be String values, or objects of this class
 * <p/> fromXml() and toXml() methods allow 'visible' representations of objects of this class
 * and run time loading/dumping from/to standard XML documents *
 * 
 */
public class MetaDataConfig implements Serializable, Cloneable {

	private static java.util.logging.Logger logger = LoggerADP.getLogger(MetaDataConfig.class.getSimpleName());
    
    private static final long serialVersionUID = 1L;
    
    private Map<String,String> 	_attributes;

    /**
     * Constructor of a root node (an uppermost ConfigTree)
     *
     * @param name String - the element name of 'this'
     */
    public MetaDataConfig() {

    }

    /**
     * assign a value to a named attribute
     *
     * @param name  String - the name (key) for the new attribute
     * @param value String - the value assigned to the key (if null - old value will be deleted)
     * @return String - old value assigned to the name (null if there was none)
     */
    public String setAttribute(String name, String value) {
        if (null == name)
            throw new IllegalArgumentException("Attribute name must be non null");
        if (null == _attributes)
            _attributes = new HashMap<String, String>();
        String oldVal = _attributes.remove(name);
        if (null != value)
            _attributes.put(name, value);
        return oldVal;
    } 

    /**
     * @return int - the number of non null attributes that this node has been assigned
     */
    public int attributeCount() {
        return (null == _attributes) ? 0 : _attributes.size();
    } 

    /**
     * Retrieve the value assigned to an attribute key.
     *
     * @param name String - the search key.
     * @return String - the value assigned to the specified key, or null if the
     *         attribute is not defined.
     */
    public String getAttribute(String name) {
        return (null == _attributes) ? null : _attributes.get(name);
    } 

    /**
     * Retrieve the value assigned to an attribute key, returning the supplied default
     * if the attribute is not defined.
     *
     * @param name         String - the search key.
     * @param defaultValue String - the default value to return if attribute is not set.
     * @return String - the value assigned to the specified key, or the default if
     *         the value is not defined.
     */
    public String getAttribute(String name, String defaultValue) {
        String ret = (null == _attributes) ? null : _attributes.get(name);
        return (ret != null ? ret : defaultValue);
    } 

    public long getLongAttribute(String name, long defaultValue) {
        String value = getAttribute(name);

        if(value != null) {
            try {
                return Long.parseLong(value.trim());
            } catch(NumberFormatException e) {
                logger.log(Level.SEVERE, "Invalid value '" + value + "' for property '" + name + "'.  Must be an long/integer value.  Returning default value '" + defaultValue + "'.");
            }
        }

        return defaultValue;
    }

    public float getFloatAttribute(String name, float defaultValue) {
        String value = getAttribute(name);

        if(value != null) {
            try {
                return Float.parseFloat(value.trim());
            } catch(NumberFormatException e) {
            	 logger.log(Level.SEVERE, "Invalid value '" + value + "' for property '" + name + "'.  Must be an float value.  Returning default value '" + defaultValue + "'.");
            }
        }

        return defaultValue;
    }

    public boolean getBooleanAttribute(String name, boolean defaultValue) {
        String value = getAttribute(name);

        if(value != null) {
            try {
                return Boolean.parseBoolean(value.trim());
            } catch(NumberFormatException e) {
            	 logger.log(Level.SEVERE, "Invalid value '" + value + "' for property '" + name + "'.  Must be an boolean value.  Returning default value '" + defaultValue + "'.");
            }
        }

        return defaultValue;
    }

    /**
     * Get the value of a requred property, throwing a {@link ConfigurationException}
     * if the property is not defined.
     *
     * @param name String - the search key.
     * @return The value assigned to the specified property.
     * @throws ConfigurationException The propery is not defined.
     */
    public String getRequiredAttribute(String name) throws ConfigurationException {
        String ret = getAttribute(name);

        if (ret == null) {
            throw new ConfigurationException("Required configuration property '" + name + "' not defined on configuration.");
        }

        return ret;
    }

    /**
     * obtain the list of all attribute names
     *
     * @return Set<String>  - the set of keys that have been assigned a non null value
     */
    public Set<String> getAttributeNames() {
        return (null == _attributes)
                ? new HashSet<String>()
                : _attributes.keySet();
    } 

    /**
     * obtain the list of all attribute as a List<KeyValuePair>
     *
     * @return List<KeyValuePair> - containing all attributes
     */
    public List<KeyValuePair> attributesAsList() {
        List<KeyValuePair> oRet = new ArrayList<KeyValuePair>();
        if (null != _attributes)
            for (Map.Entry<String, String> oCurr : _attributes.entrySet())
                oRet.add(new KeyValuePair(oCurr.getKey(), oCurr.getValue()));
        return oRet;
    } 

    
    @Override
    public Object clone() {
        return cloneObj();
    }

    /**
     * instantiate a new ConfigTree with the same topology and contents of 'this'
     * <br/>Contained ConfigTree child elements will also be cloned
     * <br/>Transient objects are NOT copied nor cloned
     *
     * @return ConfigTree - Deep copy of 'this'
     */
    public MetaDataConfig cloneObj() {
        return cloneObj(null);
    }

    /**
     * @return ConfigTree - Deep copy of 'this'
     */
    public MetaDataConfig cloneObj(MetaDataConfig dad) {
        MetaDataConfig oRet = new MetaDataConfig();
        if (null != _attributes)
            for (Map.Entry<String, String> oAtt : _attributes.entrySet())
                oRet.setAttribute(oAtt.getKey(), oAtt.getValue());
        return oRet;
    }

    /**
     * 
     * @param key
     * @return true if the attribute with name key is present on the hash
     */
	public boolean containsAttribute(String key) {
		return _attributes.containsKey(key);
	} 

} 
